home *** CD-ROM | disk | FTP | other *** search
/ Windows Expert / Windows Expert.iso / windownt / ntls.zip / LS.CPP next >
C/C++ Source or Header  |  1993-03-06  |  18KB  |  572 lines

  1. // ls - Windows NT directory listing utility
  2. //
  3. // Author: Jon S. Whalen (c) 1993
  4. //   internet: jon@hook.corp.mot.com
  5. //   compuserve: 76665,3043
  6. //
  7. // 6 March 1993
  8. // Release 1.0 'ls.exe'
  9.  
  10. #include <windef.h>
  11. #include <excpt.h>
  12. #include <winbase.h>
  13. #include <iostream.h>
  14. #include <stdlib.h>
  15. #include <winuser.h>
  16. //#include <wincon.h>
  17.  
  18. // the structs and functions are declared in
  19. // wincon.h, but not for C++, so they're included here to get
  20. // the linkage to work.
  21.  
  22. typedef struct _COORD {
  23.     SHORT X;
  24.     SHORT Y;
  25. } COORD, *PCOORD;
  26.  
  27. typedef struct _SMALL_RECT {
  28.     SHORT Left;
  29.     SHORT Top;
  30.     SHORT Right;
  31.     SHORT Bottom;
  32. } SMALL_RECT, *PSMALL_RECT;
  33.  
  34. typedef struct _CONSOLE_SCREEN_BUFFER_INFO {
  35.     COORD dwSize;
  36.     COORD dwCursorPosition;
  37.     WORD  wAttributes;
  38.     SMALL_RECT srWindow;
  39.     COORD dwMaximumWindowSize;
  40. } CONSOLE_SCREEN_BUFFER_INFO, *PCONSOLE_SCREEN_BUFFER_INFO;
  41.  
  42. extern "C" BOOL WINAPI GetConsoleScreenBufferInfo( HANDLE, PCONSOLE_SCREEN_BUFFER_INFO );
  43. extern "C" BOOL WINAPI SetConsoleCursorPosition( HANDLE, COORD );
  44.  
  45. // error/usage message
  46.  
  47. void Usage( void )
  48. {
  49.    cerr << "\nProgram: \"ls.exe\" - Windows NT directory list utility - Version 1.0" << endl;
  50.    cerr << "   Copyright (c) Jon S. Whalen 1993. All rights reserved." << endl;
  51.    cerr << "\nusage: ls [-1?alstzLSTFZ] [path|file|pattern]\n" << endl;
  52.    cerr << "   ?|h: display this usage message" << endl;
  53.    cerr << "   1:   format as single column, left adjusted" << endl;
  54.    cerr << "   a:   show all files (including .* and hidden files)" << endl;
  55.    cerr << "   F:   add suffixes to the file names:" << endl;
  56.    cerr << "        '\\' = directory" << endl;
  57.    cerr << "        '!' = hidden file (only when -a is specified)" << endl;
  58.    cerr << "        '&' = system file" << endl;
  59.    cerr << "   l:   use long listing format" << endl;
  60.    cerr << "   L:   use longer listing format" << endl;
  61.    cerr << "   s:   sort by size - decreasing" << endl;
  62.    cerr << "   S:   sort by size - increasing" << endl;
  63.    cerr << "   t:   sort by modification time - newest first" << endl;
  64.    cerr << "   T:   sort by modification time - oldest first" << endl;
  65.    cerr << "   z:   sort by name - lexical order (i.e. alphabetical order)" << endl;
  66.    cerr << "   Z:   sort by name - reverse lexical order" << endl;
  67.    cerr << "\nNotes:\n" << endl;
  68.    cerr << "   1. For compatibility with Unix ls, file and directory names beginning" << endl;
  69.    cerr << "      with '.' are not displayed unless the -a option is specified." << endl;
  70.    cerr << "   2. Specifiying only a drive name (e.g. \"ls C:\") will list the contents" << endl;
  71.    cerr << "      of the current working directory for that drive." << endl;
  72.    cerr << "   3. File systems which are case-insensitive (e.g. FAT) are displayed" << endl;
  73.    cerr << "      in lower case. Case-sensitive file systems (e.g. NTFS) are displayed" << endl;
  74.    cerr << "      in mixed upper and lower case. It is Unicode compatible." << endl;
  75.    cerr << "   4. This program is freely distributable and is provided \"AS-IS\", WITHOUT" << endl;
  76.    cerr << "      WARRANTY of any kind, either express or implied. No guarantee is made" << endl;
  77.    cerr << "      of suitablility for any purpose, whatsoever." << endl;
  78. }
  79.  
  80. struct FileListEl {
  81.    WIN32_FIND_DATA *lpwfd;
  82.    FileListEl *next, *prev;
  83.    FileListEl( void ) : lpwfd(0), next(0), prev(0) { }
  84.    ~FileListEl( void ) { if( lpwfd ) delete lpwfd; }
  85. };
  86.  
  87. void SwapEl( FileListEl * cur, FileListEl *next )
  88. {
  89.    FileListEl * p1 = (cur?cur->prev:0);
  90.    FileListEl * p2 = cur;
  91.    FileListEl * p3 = next;
  92.    FileListEl * p4 = (p3?p3->next:0);
  93.  
  94.    if( p1 ) p1->next = p3;
  95.    p3->prev = p1;
  96.    if( p4 ) p4->prev = p2;
  97.    p2->next = p4;
  98.    p3->next = p2;
  99.    p2->prev = p3;
  100. }
  101.  
  102. int GetFileList( TCHAR * filter, FileListEl *& head, DWORD & max_filename_len )
  103. {
  104.    FileListEl * cur = 0;
  105.    WIN32_FIND_DATA * lpwfd;
  106.    int matches = 0;
  107.    DWORD len = 0;
  108.  
  109.    lpwfd = new WIN32_FIND_DATA;
  110.    HANDLE hSearch = FindFirstFile( filter, lpwfd );
  111.    if( hSearch == INVALID_HANDLE_VALUE )
  112.    {
  113.       return 0;
  114.    }
  115.  
  116.    // else
  117.    cur = head = new FileListEl;
  118.    head->lpwfd = lpwfd;
  119.    max_filename_len = lstrlen(lpwfd->cFileName);
  120.    matches = 1;
  121.  
  122.    lpwfd = new WIN32_FIND_DATA;
  123.    while( FindNextFile( hSearch, lpwfd ) )
  124.    {
  125.       cur->next = new FileListEl; cur->next->prev = cur; cur = cur->next;
  126.       cur->lpwfd = lpwfd;
  127.  
  128.       len = lstrlen(lpwfd->cFileName);
  129.       if( max_filename_len < len ) max_filename_len = len;
  130.  
  131.       lpwfd = new WIN32_FIND_DATA;
  132.       matches++;
  133.    }
  134.    delete lpwfd;
  135.  
  136.    FindClose( hSearch );
  137.  
  138.    return matches;
  139. }
  140.  
  141. int main( int argc, TCHAR **argv )
  142. {
  143.    CONSOLE_SCREEN_BUFFER_INFO csbi;
  144.    TIME_ZONE_INFORMATION tzi;
  145.  
  146.    TCHAR filter[512]; filter[0] = 0;
  147.    TCHAR cur_directory[512]; cur_directory[0] = 0;
  148.    TCHAR new_directory[512]; new_directory[0] = 0;
  149.    TCHAR * cp = 0;
  150.  
  151.    DWORD len = 0;
  152.    DWORD maxlen = 512;
  153.    DWORD max_filename_len = 0;
  154.    LPTSTR progname = argv[0];
  155.    FileListEl * head = 0;
  156.    FileListEl * cur = 0;
  157.    DWORD pos = 0;
  158.    int column = 0;
  159.    int num_columns = 0;
  160.  
  161.    // Flags
  162.  
  163.    int f_list_all         = 0;
  164.    int f_long_list        = 0;
  165.    int f_modify_time_sort = 0;
  166.    int f_alpha_sort       = 1;
  167.    int f_size_sort        = 0;
  168.    int f_list_with_suffix = 0;
  169.    int f_single_column    = 0;
  170.  
  171.    int error              = 0;
  172.    int suffixed           = 0;
  173.    int case_sensitive     = 0;
  174.    int matches            = 0;
  175.    long result            = 0;
  176.  
  177.    // open a handle to the console
  178.  
  179.    HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
  180.  
  181.    // check out the environment
  182.  
  183.    result = GetCurrentDirectory( maxlen, cur_directory );
  184.    result = GetConsoleScreenBufferInfo(hOutput, &csbi);
  185.    result = GetTimeZoneInformation(&tzi);
  186.  
  187.    DWORD line_length = csbi.dwSize.X;
  188.  
  189.    // process command line args
  190.  
  191.    argc--; argv++; // skip progname
  192.    for ( ; argc && ((*argv)[0] == L'/' || (*argv)[0] == L'-'); argv++, argc-- )
  193.    {
  194.       cp = *argv; cp++;
  195.       while( *cp )
  196.       {
  197.          switch( *cp )
  198.          {
  199.             case L'h':
  200.             case L'?':
  201.                Usage();
  202.                return -1;
  203.                break;
  204.             case L'1':
  205.                f_single_column++;
  206.                break;
  207.             case L'a':
  208.                f_list_all++;
  209.                break;
  210.             case L'l':
  211.                f_long_list = 1;
  212.                break;
  213.             case L'L':
  214.                f_long_list = 2;
  215.                break;
  216.             case L's':
  217.                f_size_sort = 1;
  218.                f_alpha_sort = 0;
  219.                f_modify_time_sort = 0;
  220.                break;
  221.             case L'S':
  222.                f_size_sort = -1;
  223.                f_alpha_sort = 0;
  224.                f_modify_time_sort = 0;
  225.                break;
  226.             case L't':
  227.                f_modify_time_sort = 1;
  228.                f_alpha_sort = 0;
  229.                f_size_sort = 0;
  230.                break;
  231.             case L'T':
  232.                f_modify_time_sort = -1;
  233.                f_alpha_sort = 0;
  234.                f_size_sort = 0;
  235.                break;
  236.             case L'F':
  237.                f_list_with_suffix++;
  238.                break;
  239.             case L'z':
  240.                f_alpha_sort = 1;
  241.                f_modify_time_sort = 0;
  242.                f_size_sort = 0;
  243.                break;
  244.             case L'Z':
  245.                f_alpha_sort = -1;
  246.                f_modify_time_sort = 0;
  247.                f_size_sort = 0;
  248.                break;
  249.             default:
  250.                cerr << "ls: illegal option -- " << *cp << endl;
  251.                error++;
  252.          }
  253.          cp++;
  254.       }
  255.    }
  256.  
  257.    if( error )
  258.    {
  259.       Usage( );
  260.       return -1;
  261.    }
  262.  
  263.    if( argc == 0 )
  264.    {
  265.       filter[0] = L'*';
  266.       filter[1] = L'.';
  267.       filter[2] = L'*';
  268.       filter[3] = 0;
  269.    }
  270.    else
  271.    {
  272.       // check to see if the argument specifies a path
  273.  
  274.       lstrcpy( filter, *argv );
  275.       long index = lstrlen( filter );
  276.       switch( filter[index - 1] )
  277.       {
  278.          case L':':
  279.          case L'\\':
  280.             filter[index] = L'*';
  281.             filter[index + 1] = L'.';
  282.             filter[index + 2] = L'*';
  283.             filter[index + 3] = 0;
  284.             break;
  285.       }
  286.  
  287.    }
  288.  
  289.    // get drive information
  290.  
  291.    DWORD dwMaxFilename, dwFlags;
  292.    TCHAR file_system_name[32];
  293.    TCHAR volume_name[128];
  294.    DWORD sectors_per_cluster, bytes_per_sector, free_clusters, total_clusters;
  295.    DWORD free_bytes, total_bytes;
  296.  
  297.    if( filter[1] == L':' )
  298.    {
  299.       TCHAR tmp[4];
  300.       tmp[0] = filter[0]; tmp[1] = filter[1]; tmp[2] = L'\\'; tmp[3] = 0;
  301.       GetVolumeInformation( tmp, volume_name, 128, 0, &dwMaxFilename, &dwFlags, file_system_name, 32 );
  302.       GetDiskFreeSpace( tmp, §ors_per_cluster, &bytes_per_sector, &free_clusters, &total_clusters);
  303.    }
  304.    else
  305.    {
  306.       GetVolumeInformation( 0, volume_name, 128, 0, &dwMaxFilename, &dwFlags, file_system_name, 32 );
  307.       GetDiskFreeSpace( 0, §ors_per_cluster, &bytes_per_sector, &free_clusters, &total_clusters);
  308.    }
  309.  
  310.    if( dwFlags & FS_CASE_IS_PRESERVED )
  311.       case_sensitive++;
  312.    total_bytes = total_clusters * sectors_per_cluster * bytes_per_sector;
  313.    free_bytes = free_clusters * sectors_per_cluster * bytes_per_sector;
  314.  
  315.    // first find all matching files
  316.  
  317.    matches = GetFileList( filter, head, max_filename_len );
  318.    if( !matches )
  319.    {
  320.       cout << filter << " not found." << endl;
  321.       return 0;
  322.    }
  323.    else if( matches == 1 && (head->lpwfd->dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) )
  324.    {
  325.       delete head; head = 0;
  326.  
  327.       // the filter was a directory name, add \*.* to the filter
  328.  
  329.       long index = lstrlen( filter );
  330.       filter[index] = L'\\';
  331.       filter[index + 1] = L'*';
  332.       filter[index + 2] = L'.';
  333.       filter[index + 3] = L'*';
  334.       filter[index + 4] = 0;
  335.  
  336.       // now try again
  337.  
  338.       matches = GetFileList( filter, head, max_filename_len );
  339.    }
  340.  
  341.    if( f_single_column )
  342.       num_columns = 1;
  343.    else
  344.       num_columns = line_length/(max_filename_len + 4);
  345.  
  346.    // now sort
  347.  
  348.    if( f_modify_time_sort )
  349.    {
  350.       // this is a dumb sorting algorithm -- I'll replace it soon?
  351.       int swapped_one = 1;
  352.  
  353.       while( swapped_one )
  354.       {
  355.          swapped_one = 0;
  356.          for( cur = head; cur && cur->next; cur = cur->next )
  357.          {
  358.             result = CompareFileTime( &(cur->lpwfd->ftLastWriteTime), &(cur->next->lpwfd->ftLastWriteTime) );
  359.  
  360.             if( f_modify_time_sort < 0 ) result *= -1;
  361.  
  362.             if( result == -1 )
  363.             {
  364.                // swap the entries
  365.                swapped_one++;
  366.                FileListEl * next = cur->next;
  367.                SwapEl( cur, next );
  368.                if( head == cur ) head = next;
  369.                cur = next;
  370.             }
  371.          }
  372.       }
  373.    }
  374.    else if( f_size_sort )
  375.    {
  376.       // this is a dumb sorting algorithm -- I'll replace it soon?
  377.       int swapped_one = 1;
  378.  
  379.       while( swapped_one )
  380.       {
  381.          swapped_one = 0;
  382.          for( cur = head; cur && cur->next; cur = cur->next )
  383.          {
  384.             if( (cur->lpwfd->nFileSizeLow) < (cur->next->lpwfd->nFileSizeLow) )
  385.                result = -1;
  386.             else if ( (cur->next->lpwfd->nFileSizeLow) == (cur->lpwfd->nFileSizeLow) )
  387.                result = 0;
  388.             else
  389.                result = 1;
  390.  
  391.             if( f_size_sort < 0 ) result *= -1;
  392.  
  393.             if( result == -1 )
  394.             {
  395.                // swap the entries
  396.                swapped_one++;
  397.                FileListEl * next = cur->next;
  398.                SwapEl( cur, next );
  399.                if( head == cur ) head = next;
  400.                cur = next;
  401.             }
  402.          }
  403.       }
  404.    }
  405.    else if( f_alpha_sort )
  406.    {
  407.       // this is a dumb sorting algorithm -- I'll replace it soon?
  408.       int swapped_one = 1;
  409.  
  410.       while( swapped_one )
  411.       {
  412.          swapped_one = 0;
  413.          for( cur = head; cur && cur->next; cur = cur->next )
  414.          {
  415.             result = lstrcmp( (cur->next->lpwfd->cFileName), (cur->lpwfd->cFileName) );
  416.  
  417.             if( f_alpha_sort < 0 ) result *= -1;
  418.  
  419.             if( result == -1 )
  420.             {
  421.                // swap the entries
  422.                swapped_one++;
  423.                FileListEl * next = cur->next;
  424.                SwapEl( cur, next );
  425.                if( head == cur ) head = next;
  426.                cur = next;
  427.             }
  428.          }
  429.       }
  430.    }
  431.  
  432.    // print the listing
  433.  
  434.    if ( f_long_list )
  435.    {
  436.       if( f_long_list > 1 )
  437.       {
  438.          cout << "\nVolume Name: \"" << volume_name << "\"  File System Type: [" << file_system_name << "]" << endl;
  439.          cout << "\n[Attribs]    [Size]      [Created]       [Last Modified]   [Name]" << endl;
  440.       }
  441.       else
  442.          cout << "\n[Attribs]    [Size]   [Last Modified]   [Name]" << endl;
  443.  
  444.       for( cur = head; cur; cur = cur->next )
  445.       {
  446.          if ( f_list_all || (cur->lpwfd->cFileName[0] != L'.' && !(cur->lpwfd->dwFileAttributes&FILE_ATTRIBUTE_HIDDEN)) )
  447.          {
  448.             SYSTEMTIME stm;
  449.             TCHAR buffer[128];
  450.  
  451.             // attributes
  452.  
  453.             cout << (CHAR)((cur->lpwfd->dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) ? 'd':'-');
  454.             cout << (CHAR)((cur->lpwfd->dwFileAttributes&FILE_ATTRIBUTE_HIDDEN)    ? 'h':'-');
  455.             cout << (CHAR)((cur->lpwfd->dwFileAttributes&FILE_ATTRIBUTE_SYSTEM)    ? 's':'-');
  456.         cout << (CHAR)((cur->lpwfd->dwFileAttributes&FILE_ATTRIBUTE_READONLY)  ? 'r':'-');
  457.         cout << (CHAR)((cur->lpwfd->dwFileAttributes&FILE_ATTRIBUTE_ARCHIVE)   ? 'a':'-');
  458.  
  459.         // attributes for NTFS only
  460.  
  461.         cout << (CHAR)((cur->lpwfd->dwFileAttributes&FILE_ATTRIBUTE_TEMPORARY)     ? 't':'-');
  462.         cout << (CHAR)((cur->lpwfd->dwFileAttributes&FILE_ATTRIBUTE_ATOMIC_WRITE)  ? 'm':'-');
  463.         cout << (CHAR)((cur->lpwfd->dwFileAttributes&FILE_ATTRIBUTE_XACTION_WRITE) ? 'x':'-');
  464.         cout << " ";
  465.  
  466.         // size
  467.  
  468.         wsprintf( buffer, "%wc%9d ", ((cur->lpwfd->nFileSizeHigh)?L'+':L' '), cur->lpwfd->nFileSizeLow );
  469.         cout << buffer;
  470.  
  471.             if( f_long_list > 1 )
  472.             {
  473.                // creation time
  474.  
  475.            FileTimeToSystemTime( &(cur->lpwfd->ftCreationTime), &stm);
  476.            wsprintf( buffer, " %02d-%02d-%02d %02d:%02d:%02d ", stm.wMonth, stm.wDay, (stm.wYear%100), (stm.wHour - tzi.Bias/60), stm.wMinute, stm.wSecond );
  477.            cout << buffer;
  478.  
  479.                // last access time
  480.  
  481.            //FileTimeToSystemTime( &(cur->lpwfd->ftLastAccessTime), &stm);
  482.            //wsprintf( buffer, " %02d-%02d-%02d %02d:%02d:%02d ", stm.wMonth, stm.wDay, (stm.wYear%100), (stm.wHour - tzi.Bias/60), stm.wMinute, stm.wSecond );
  483.            //cout << buffer;
  484.             }
  485.  
  486.         // last modification time
  487.  
  488.         FileTimeToSystemTime( &(cur->lpwfd->ftLastWriteTime), &stm);
  489.         wsprintf( buffer, " %02d-%02d-%02d %02d:%02d:%02d  ", stm.wMonth, stm.wDay, (stm.wYear%100), (stm.wHour - tzi.Bias/60), stm.wMinute, stm.wSecond );
  490.         cout << buffer;
  491.  
  492.         cout << (case_sensitive?cur->lpwfd->cFileName:CharLower(cur->lpwfd->cFileName));
  493.         if( f_list_with_suffix )
  494.         {
  495.                if(cur->lpwfd->dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
  496.                   cout << L'\\';
  497.            else
  498.            {
  499.                   if(cur->lpwfd->dwFileAttributes&FILE_ATTRIBUTE_HIDDEN)
  500.                      cout << L'!';
  501.                   if(cur->lpwfd->dwFileAttributes&FILE_ATTRIBUTE_SYSTEM)
  502.                      cout << L'&';
  503.                }
  504.             }
  505.         cout << endl;
  506.      }
  507.       }
  508.  
  509.       if( f_long_list > 1 )
  510.       {
  511.          cout << "\nVolume Statistics:" << endl;
  512.          cout << "\n[Size] " << total_bytes << "  [Free] " << free_bytes <<"  [% Used] " << 100.00*(long)(total_bytes - free_bytes)/(long)total_bytes << "." << endl;
  513.          //cout << "bytes used:  " << (total_bytes - free_bytes)  endl;
  514.       }
  515.    } // end-if
  516.  
  517.    else
  518.    for( cur = head; cur; cur = cur->next )
  519.    {
  520.       if ( f_list_all || (cur->lpwfd->cFileName[0] != L'.' && !(cur->lpwfd->dwFileAttributes&FILE_ATTRIBUTE_HIDDEN)) )
  521.       {
  522.          len = lstrlen(cur->lpwfd->cFileName);
  523.          if( column == num_columns )
  524.          {
  525.             cout << endl;
  526.             column = 1;
  527.          }
  528.          else
  529.             column++;
  530.  
  531.          // filename
  532.  
  533.          cout << (case_sensitive?cur->lpwfd->cFileName:CharLower(cur->lpwfd->cFileName));
  534.          pos += len;
  535.  
  536.          // suffix
  537.  
  538.          suffixed = 0;
  539.          if( f_list_with_suffix )
  540.          {
  541.             if(cur->lpwfd->dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
  542.             {
  543.                cout << L'\\';
  544.                suffixed++;
  545.             }
  546.             else
  547.             {
  548.                if(cur->lpwfd->dwFileAttributes&FILE_ATTRIBUTE_HIDDEN)
  549.                {
  550.                   cout << L'!';
  551.                   suffixed++;
  552.                }
  553.                if(cur->lpwfd->dwFileAttributes&FILE_ATTRIBUTE_SYSTEM)
  554.                {
  555.                   cout << L'&';
  556.                   suffixed++;
  557.                }
  558.             }
  559.             pos++;
  560.          }
  561.  
  562.          // padding
  563.  
  564.          len = max_filename_len - len + 4 - suffixed;
  565.          if( column < num_columns )
  566.             while( len-- ) cout << ' ';
  567.       }
  568.    }
  569.  
  570.    return 0;
  571. }
  572.